package tools;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.temporal.TemporalAdjusters;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;


import static java.util.Calendar.getInstance;

@Slf4j
public class DateUtils {

    /**
     * 两时间段 重合的天数,含前含后
     * 这里共有2个时间段（b1-----e1）【b2-----e2】，4个时间点；
     * 相当于两条线段(b代表起点，e代表端点，b<=e)，4个端点。
     * <p/>
     * 可分3种情况：
     * 1.包含(两条)
     * （b1---【b2-----e2】--e1）   if(b1<=b2 && e1>=e2)
     * 【b2---（b1-----e1）--e2】   if(b1>=b2&&e1<=e2)
     * <p/>
     * 2.相交(两条)
     * （b1---【b2---e1）----e2】    if(b1<b2&&e1<e2&&e1>b2)
     * 【b2---(b1---e2】----e1)	 if(b1>b2&&b1<e2&&e2<e1)
     * <p/>
     * 3.不相交
     * （b1-----e1）【b2-----e2】或【b2-----e2】（b1-----e1）if(e1<b2||b1>e2)此时，重合时间数为零。
     *
     * @param beginTime1
     * @param endTime1
     * @param beginTime2
     * @param endTime2
     * @return
     */
    public static int getTimeDiffDay(Date beginTime1, Date endTime1, Date beginTime2, Date endTime2) {
        long b1 = beginTime1.getTime();
        long e1 = endTime1.getTime();
        long b2 = beginTime2.getTime();
        long e2 = endTime2.getTime();

        int coincidenceTime;
        if (b1 <= b2 && e1 >= e2) {//（b1---【b2-----e2】--e1）1包含2
            coincidenceTime = getDayBetween(endTime2, beginTime2) + 1;
        } else if (b1 >= b2 && e1 <= e2) {//【b2---（b1-----e1）--e2】2包含1
            coincidenceTime = getDayBetween(endTime1, beginTime1) + 1;
        } else if (b1 >= b2 && b1 <= e2 && e2 <= e1) {//（b1---【b2---e1）----e2】 相交1
            coincidenceTime = getDayBetween(endTime2, beginTime1) + 1;
        } else if (b1 <= b2 && e1 <= e2 && e1 >= b2) {//【b2---(b1---e2】----e1) 相交2
            coincidenceTime = getDayBetween(endTime1, beginTime2) + 1;
        } else if (e1 == b2 || b1 == e2) {//1天交际
            coincidenceTime = 1;
        } else if (e1 < b2 || b1 > e2) {//（b1-----e1）【b2-----e2】或【b2-----e2】（b1-----e1）不相交
            coincidenceTime = 0;
        } else {
            coincidenceTime = 0;
            log.info("意料外的日期组合，无法计算重合！");
        }
        return coincidenceTime;
    }

    /**
     * 比较两个字符串日期 yyyy-MM-dd 大小
     */
    public static int compareStringDate(String date1, String date2) {
        Long d1 = Long.valueOf(date1.replaceAll("-", ""));
        Long d2 = Long.valueOf(date2.replaceAll("-", ""));
        return d1.compareTo(d2);
    }

    /**
     * 比较第一个日期的下一天是否是第二个日期
     *
     * @param date1 第一个日期
     * @param date2 第二个日期
     * @return 是-true 否-false
     */
    public static boolean equalsNextDay(String date1, String date2) {
        Date date = DateUtils.setDate(date1, 1);
        return date.equals(DateUtils.stringToDate(date2));
    }

    /**
     * string date 的下一个日期
     *
     * @param date date 字符串
     * @return a {@link String} 下一个日期
     */
    public static String getNextDay(String date) {
        Date temp = DateUtils.setDate(date, 1);
        return DateUtils.dateToString(temp, "yyyy-MM-dd");
    }

    /**
     * string date 的上一个日期
     *
     * @param date date 字符串
     * @return a {@link String} 上一个日期
     */
    public static String getBeforeDay(String date) {
        Date temp = DateUtils.setDate(date, -1);
        return DateUtils.dateToString(temp, "yyyy-MM-dd");
    }

    /**
     * 添加几个月之后的日期, 此方法针对2月份做过处理
     *
     * @param date
     * @param month
     * @return
     */
    public static String getDateStrLastMonth(Date date, int month) {
        return DateUtils.dateToString(getDateStrLastMonthDate(date, month), "yyyy-MM-dd");
    }

    /**
     * 添加几个月之后的日期, 此方法针对2月份做过处理
     *
     * @param date
     * @param month
     * @return
     */
    public static Date getDateStrLastMonthDate(Date date, int month) {
        Calendar c = Calendar.getInstance();
        c.setTime(date);
        c.set(Calendar.MONTH, c.get(Calendar.MONTH) + month);
        //处理2月份的特殊情况
        int days = c.get(Calendar.DAY_OF_MONTH);
        int months = c.get(Calendar.MONTH);
        //3.2,3.3 计算后的日期是3.2号,判断是否真正的是3.2号
        if (months == 2 && (days == 2 || days == 3)) {
            Calendar old = Calendar.getInstance();
            old.setTime(date);
            if (old.get(Calendar.DAY_OF_MONTH) > 29) {
                c.set(Calendar.DAY_OF_MONTH, 1);
            } else if (old.get(Calendar.DAY_OF_MONTH) == 29) {
                if (!isLeapYear(c.get(Calendar.YEAR))) {
                    c.set(Calendar.DAY_OF_MONTH, 1);
                }
            }
        }
        return c.getTime();
    }

    public static int getBetweenDays(Date date1, Date date2) {

        Calendar before = Calendar.getInstance();
        Calendar after = Calendar.getInstance();
        if (date1.after(date2)) {
            before.setTime(date2);
            after.setTime(date1);
        } else {
            before.setTime(date1);
            after.setTime(date2);
        }
        int days = 0;
        int startDay = before.get(Calendar.DAY_OF_YEAR);
        int endDay = after.get(Calendar.DAY_OF_YEAR);
        int startYear = before.get(Calendar.YEAR);
        int endYear = after.get(Calendar.YEAR);
        before.clear();
        before.set(startYear, Calendar.JANUARY, 1);

        while (startYear != endYear) {
            before.set(startYear++, Calendar.DECEMBER, 31);
            days += before.get(Calendar.DAY_OF_YEAR);
        }
        int rtn = days + endDay - startDay;
        if (date1.after(date2)) {
            rtn = -rtn;
        }
        return rtn;
    }


    /**
     * 是否闰年 2月份 29天
     *
     * @param year
     * @return
     */
    public static boolean isLeapYear(int year) {
        if (year % 100 == 0) {
            if (year % 400 == 0) {
                return true;
            }
        } else {
            if (year % 4 == 0) {
                return true;
            }
        }
        return false;
    }


    /**
     * 判断字符串是不是日期格式
     */
    public static boolean checkDate(String dateStr) {
        String eL = "^((\\d{2}(([02468][048])|([13579][26]))[\\-\\/\\s]?((((0?[13578])|(1[02]))[\\-\\/\\s]?((0?[1-9])|([1-2][0-9])|(3[01])))|(((0?[469])|(11))[\\-\\/\\s]?((0?[1-9])|([1-2][0-9])|(30)))|(0?2[\\-\\/\\s]?((0?[1-9])|([1-2][0-9])))))|(\\d{2}(([02468][1235679])|([13579][01345789]))[\\-\\/\\s]?((((0?[13578])|(1[02]))[\\-\\/\\s]?((0?[1-9])|([1-2][0-9])|(3[01])))|(((0?[469])|(11))[\\-\\/\\s]?((0?[1-9])|([1-2][0-9])|(30)))|(0?2[\\-\\/\\s]?((0?[1-9])|(1[0-9])|(2[0-8]))))))";
        Pattern p = Pattern.compile(eL);
        Matcher m = p.matcher(dateStr);
        return m.matches();
    }

    /**
     * 检验是否满足yyyy-mm-dd格式的日期
     *
     * @param dateStr
     * @return
     */
    public static boolean valiDateStr(String dateStr) {
        Pattern pattern = Pattern.compile("^\\d{4}\\-\\d{2}\\-\\d{2}$");
        return pattern.matcher(dateStr).matches();
    }

    //获取12个月份,从当前月份开始
    public static List<Integer> get12Months() {
        int m = Calendar.getInstance().get(Calendar.MONTH) + 1;
        List<Integer> rtn = new ArrayList<>();
        for (int i = 0, k = 0; i < 12; i++) {
            if (m <= 12) {
                rtn.add(m++);
            } else {
                rtn.add(++k);
            }
        }
        return rtn;
    }

    /**
     * 两时间段 重合的天数,含前含后
     *
     * @param start1
     * @param end1
     * @param start2
     * @param end2
     * @return
     */
    public static int getTimeDiffDay(String start1, String end1, String start2, String end2) {
        Date beginTime1 = stringToDate(start1);
        Date endTime1 = stringToDate(end1);
        Date beginTime2 = stringToDate(start2);
        Date endTime2 = stringToDate(end2);
        return getTimeDiffDay(beginTime1, endTime1, beginTime2, endTime2);
    }

    //获取日期的最后一天
    public static int getLastDay(Date date) {
        Calendar ca = getInstance();
        ca.setTime(date);
        ca.set(Calendar.DAY_OF_MONTH, ca.getActualMaximum(Calendar.DAY_OF_MONTH));
        int lastDay = ca.get(Calendar.DAY_OF_MONTH);
        return lastDay;
    }

    //yyyy-MM-dd
    public static int getLastDay(String date) {
        return getLastDay(stringToDate(date, DATE_FORMAT));
    }

    //获取日期的最后一天日期
    public static String getLastDate(Date date) {
        Calendar ca = getInstance();
        ca.setTime(date);
        ca.set(Calendar.DAY_OF_MONTH, ca.getActualMaximum(Calendar.DAY_OF_MONTH));
        return dateToString(ca.getTime(), DATE_FORMAT);
    }

    //yyyy-MM-dd 获取日期的第一天日期
    public static String getFirstDate(Date date) {
        Calendar ca = getInstance();
        ca.setTime(date);
        ca.set(Calendar.DAY_OF_MONTH, 1);
        return dateToString(ca.getTime(), DATE_FORMAT);
    }

    /**
     * 获取 日期+当前时间的date
     *
     * @param YYYY_MM_DD 日期
     * @return
     */
    public static Date getYMDNowTime(String YYYY_MM_DD) {
        String HH_mm_ss = dateToString(new Date(), "HH:mm:ss");
        Date date = stringToDate(YYYY_MM_DD + " " + HH_mm_ss, DATETIME_FORMAT);
        return date;
    }

    public static long getTimeDiffHour(Date begin, Date end) {
        return (end.getTime() - begin.getTime()) / (1000 * 3600);
    }

    public static long getTimeDiffMin(Date begin, Date end) {
        return (end.getTime() - begin.getTime()) / (1000 * 60);
    }

    /**
     * 获取当前月份
     *
     * @return
     */
    public static int getCurrentMonth() {
        Calendar calendar = getInstance();
        return calendar.get(Calendar.MONTH) + 1;
    }


    /**
     * 获取当前年份
     *
     * @return
     */
    public static int getCurrentYear() {
        Calendar calendar = getInstance();
        return calendar.get(Calendar.YEAR);
    }


    /**
     * 获取本月第一天
     * 十分秒归零
     *
     * @param date 日期
     * @return Date
     */
    public static Date getMonthFirstDay(Date date) {
        if (Objects.isNull(date)) {
            return null;
        }
        Calendar c = Calendar.getInstance();
        c.setTime(date);
        c.add(Calendar.MONTH, 0);
        c.set(Calendar.DAY_OF_MONTH, 1);//设置为1号,当前日期既为本月第一天
        c.set(Calendar.HOUR, 0);
        c.set(Calendar.MINUTE, 0);
        c.set(Calendar.SECOND, 0);
        return c.getTime();
    }

    /**
     * 日期格式yyyy-MM-dd字符串常量
     */
    public static final String DATE_FORMAT = "yyyy-MM-dd";
    public static final String DATE_FORMAT_ZH = "yyyy年MM月dd日";
    public static final String DATE_FORMAT_A = "yyyyMMddHHmm";
    public static final String DATE_FORMAT_B = "yyyyMMddHH";

    public static final String DATE_FORMAT_NORMAL = "yyyyMMdd";
    public static final String DATE_FORMAT_TIME = "HHmmss";

    public static final String DATE_FORMAT_MONTH = "MM-dd HH:mm";

    /**
     * 日期格式yyyy-MM-dd HH:mm:ss字符串常量
     */
    public static final String DATETIME_FORMAT = "yyyy-MM-dd HH:mm:ss";
    public static final String DATETIME_FORMAT_SF = "yyyy-MM-dd HH:mm";
    public static final String DATETIME_FORMAT2 = "yyyyMMddHHmmss";
    public static final String DATETIME_FORMAT3 = "yyyyMMddHHmmssS";
    public static final String DATETIME_TIME = "HH.mm";

    /**
     * 得到当前日期的前/后　beforeDays　天的日期数
     *
     * @param beforeDays
     * @return
     */
    public static String getDate(int beforeDays) {
        Calendar c = Calendar.getInstance();
        c.setTime(new Date());
        c.add(Calendar.DAY_OF_MONTH, beforeDays);
        String a = dateToString(c.getTime(), DATE_FORMAT);
        return a;
    }

    /**
     * 得到当前日期的前/后　beforeDays　天的日期数,格式自定
     *
     * @param beforeDays
     * @param dateFormat
     * @return
     */
    public static String getDate(int beforeDays, String dateFormat) {
        Calendar c = Calendar.getInstance();
        c.setTime(new Date());
        c.add(Calendar.DAY_OF_MONTH, beforeDays);
        String a = dateToString(c.getTime(), dateFormat);
        return a;
    }

    /**
     * 得到指定日期的前/后　beforeDays　天的日期数,格式自定
     *
     * @param beforeDays
     * @param dateFormat
     * @return
     */
    public static String getDateOfCustom(int beforeDays, Date customDate, String dateFormat) {
        Calendar c = Calendar.getInstance();
        c.setTime(customDate);
        c.add(Calendar.DAY_OF_MONTH, beforeDays);
        String a = dateToString(c.getTime(), dateFormat);
        return a;
    }

    /**
     * 将日期类型转换为yyyy-MM-dd字符串
     *
     * @param dateValue
     * @return String
     */
    public static String dateToString(Date dateValue) {
        return dateToString(dateValue, DATETIME_FORMAT);
    }

    /**
     * 将时间戳转换为指定格式的字符串
     *
     * @param dateTime
     * @param format
     * @return
     */
    public static String dateTimeToString(Long dateTime, String format) {
        Date date = new Date(dateTime);
        return dateToString(date, format);
    }

    /**
     * 将日期类型转换为指定格式的字符串
     *
     * @param dateValue
     * @param format
     * @return String
     */
    public static String dateToString(Date dateValue, String format) {
        if (dateValue == null || format == null) {
            return null;
        } else {
            SimpleDateFormat dateFormat = new SimpleDateFormat(format);
            return dateFormat.format(dateValue);
        }
    }


    /**
     * 将日期类型转换为指定格式的字符串
     *
     * @param dateValue
     * @param format
     * @return String
     */
    public static String dateToString(Date dateValue, String format, Locale locale) {
        if (dateValue == null || format == null) {
            return null;
        } else {
            SimpleDateFormat dateFormat = new SimpleDateFormat(format, locale);
            return dateFormat.format(dateValue);
        }
    }

    /**
     * 将日期yyyy-MM-dd字符串转为日期类型，如果转换失败返回null
     *
     * @param stringValue
     * @return Date
     */
    public static Date stringToDate(String stringValue) {
        return stringToDate(stringValue, DATE_FORMAT);
    }

    /**
     * 将指定日期格式的字符串转为日期类型，如果转换失败返回null
     *
     * @param stringValue
     * @param format
     * @return Date
     */
    public static Date stringToDate(String stringValue, String format) {
        Date dateValue = null;
        if (stringValue != null && format != null) {
            try {
                SimpleDateFormat dateFormat = new SimpleDateFormat(format);
                dateValue = dateFormat.parse(stringValue);

            } catch (ParseException ex) {
                dateValue = null;
            }
        }
        return dateValue;
    }

    /**
     * 设置时间的日期值
     *
     * @param stringDate
     * @param num
     * @return Date
     */
    public static Date setDate(String stringDate, int num) {
        if (stringDate != null) {
            Date date = stringToDate(stringDate, "yyyy-MM-dd");
            return setDate(date, num);
        } else {
            return null;
        }
    }

    /**
     * 设置时间的日期值
     *
     * @param date
     * @param num
     * @return Date
     */
    public static Date setDate(Date date, int num) {
        Date dateValue = null;
        Calendar c = null;
        if (date != null) {
            c = Calendar.getInstance();
            c.setTime(date);
            c.add(Calendar.DAY_OF_MONTH, num);
            dateValue = c.getTime();
        }
        return dateValue;
    }

    /**
     * 设置时间：小时为单位
     *
     * @param date
     * @param num
     * @return
     */
    public static Date setDateOfHour(Date date, int num) {
        Date dateValue = null;
        Calendar c = null;
        if (date != null) {
            c = Calendar.getInstance();
            c.setTime(date);
            c.add(Calendar.HOUR_OF_DAY, num);
            dateValue = c.getTime();
        }
        return dateValue;
    }

    /**
     * 设置时间：分钟为单位
     *
     * @param date
     * @param num
     * @return
     */
    public static Date setDateOfMin(Date date, int num) {
        Date dateValue = null;
        Calendar c = null;
        if (date != null) {
            c = Calendar.getInstance();
            c.setTime(date);
            c.add(Calendar.MINUTE, num);
            dateValue = c.getTime();
        }
        return dateValue;
    }

    /**
     * 取得两个日期的时间间隔,相差的天数
     *
     * @param d1
     * @param d2
     * @return
     */
    public static int getDayBetween(Date d1, Date d2) {
        Calendar before = Calendar.getInstance();
        Calendar after = Calendar.getInstance();
        if (d1.before(d2)) {
            before.setTime(d1);
            after.setTime(d2);
        } else {
            before.setTime(d2);
            after.setTime(d1);
        }
        int days = 0;

        int startDay = before.get(Calendar.DAY_OF_YEAR);
        int endDay = after.get(Calendar.DAY_OF_YEAR);

        int startYear = before.get(Calendar.YEAR);
        int endYear = after.get(Calendar.YEAR);
        before.clear();
        before.set(startYear, 0, 1);

        while (startYear != endYear) {
            before.set(startYear++, Calendar.DECEMBER, 31);
            days += before.get(Calendar.DAY_OF_YEAR);
        }
        return days + endDay - startDay;
    }

    /**
     * 取得两个日期间的月份间隔 处理合同期限
     *
     * @param d1
     * @param d2
     * @return
     */
    public static int getMonthsBetween(Date d1, Date d2) {
        Calendar c1 = Calendar.getInstance();
        Calendar c2 = Calendar.getInstance();
        c1.setTime(d1);
        c2.setTime(d2);
        int result = (c2.get(Calendar.YEAR) - c1.get(Calendar.YEAR)) * 12 + (c2.get(Calendar.MONTH) - c1.get(Calendar.MONTH));
        //处理未满月的情况：起始天-截止天 :
        //大于1 ：不处理
        //小于1：+1
        if ((c1.get(Calendar.DAY_OF_MONTH) - c2.get(Calendar.DAY_OF_MONTH)) < 1) {
            result++;
        }
        return result;
    }

    public static Date addDay(Date myDate, int amount) {
        Calendar cal = Calendar.getInstance();
        cal.setTime(myDate);
        cal.add(Calendar.DAY_OF_MONTH, amount);
        return cal.getTime();
    }

    public static String formDate(Date date, String dateFormat) {
        SimpleDateFormat sdf = new SimpleDateFormat(dateFormat);
        return sdf.format(date);
    }

    /**
     * 获取当天的起始时间
     * @param nowDate
     * @return
     */
//    public static Date getDocumentDate(Date nowDate){
//        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd 00:00:00");
//        String now=sdf.format(nowDate);
//        Date date = null;
//        try {
//            date =sdf.parse(now);
//        } catch (ParseException e) {
//            e.printStackTrace();
//        }
//        return date;
//    }

    /**
     * 获取 month 个月后的日期
     *
     * @param localDate
     * @param months
     * @return LocalDate
     */
    public static LocalDate getMonthAfter(LocalDate localDate, int months) {
        return localDate.plusMonths(months);
    }

    /**
     * 获取 month 个月前的日期
     *
     * @param localDate
     * @param months
     * @return LocalDate
     */
    public static LocalDate getMonthBefore(LocalDate localDate, int months) {
        return localDate.minusMonths(months);
    }

    /**
     * 获取当月第一天日期
     *
     * @param localDate
     * @return LocalDate
     */
    public static LocalDate getMonthFirstDate(LocalDate localDate) {
        return LocalDate.of(localDate.getYear(), localDate.getMonth(), 1);
    }

    /**
     * 获取当月第一天日期
     *
     * @param localDate
     * @return LocalDate
     */
    public static LocalDate getMonthLastDate(LocalDate localDate) {
        return localDate.with(TemporalAdjusters.lastDayOfMonth());
    }

    /**
     * 获取 month 个月后的日期
     *
     * @param localDate
     * @param days
     * @return LocalDate
     */
    public static LocalDate getDayAfter(LocalDate localDate, int days) {
        return localDate.plusDays(days);
    }

    /**
     * 获取 month 个月前的日期
     *
     * @param localDate
     * @param days
     * @return LocalDate
     */
    public static LocalDate getDayBefore(LocalDate localDate, int days) {
        return localDate.minusDays(days);
    }

    /**
     * 日期转字符串
     *
     * @param localDate
     * @param format
     * @return String
     */
    public static String localDateToStr(LocalDate localDate, String format) {
        if (StringUtils.isBlank(format)) {
            format = DATE_FORMAT;
        }
        return localDate.format(DateTimeFormatter.ofPattern(format));
    }

    /**
     * 字符串转日期
     *
     * @param dateStr
     * @param format
     * @return LocalDate
     */
    public static LocalDate strToLocalDate(String dateStr, String format) {
        if (StringUtils.isBlank(format)) {
            format = DATE_FORMAT;
        }
        return LocalDate.parse(dateStr, DateTimeFormatter.ofPattern(format));
    }

    /**
     * 指定时间是否在区间范围内
     *
     * @param
     * @return
     */
    public static boolean betweenDate(Date date, Date startDate, Date endDate) {
        if (startDate.compareTo(date) <= 0 && endDate.compareTo(date) >= 0) {
            return true;
        }
        return false;
    }

}
